#include <AST/expr/control/IfAST.hpp>
#include <global.hpp>
#include <algorithm>

If::If(Expr *condition, Expr *thenExpr, Expr *elseExpr)
    : condition(condition), thenExpr(thenExpr), elseExpr(elseExpr) {
    // TODO: 计算函数的返回值类型
}

llvm::Value *If::codegen() {
    auto scopeGuard = theScope.guard();

    // 获取当前函数
    Function *parentFun = builder.GetInsertBlock()->getParent();

    Value *condV = condition->codegen();
    if (condV == nullptr)
        return logError("If::codegen: value of condition is null");
    if (condV->getType() != BasicType::BoolTy)
        return logError(
            "If::codegen: type of condition is not Bool in "
            + parentFun->getName().str());

    // parentFun->print(llvm::errs());

    BasicBlock *thenBlock = BasicBlock::Create(theContext, "then", parentFun);
    BasicBlock *elseBlock = BasicBlock::Create(theContext, "else");
    BasicBlock *meetBlock = BasicBlock::Create(theContext, "ifMeet");

    // parentFun->print(llvm::errs());

    // 此时插入点仍在condition
    builder.CreateCondBr(condV, thenBlock, elseBlock);

    // 切换插入点到then
    builder.SetInsertPoint(thenBlock);

    Value *thenV = nullptr;
    if (thenExpr != nullptr) {
        auto thenScopeGuard = theScope.guard();
        thenV = thenExpr->codegen(); // 此时block可能被切换
        if (thenV == nullptr) return nullptr;
    }

    // 获得最新的插入block
    thenBlock = builder.GetInsertBlock();
    // XXX: 检测最后一条语句是否br，若不是则 br meet
    if (thenBlock->getTerminator() == nullptr) builder.CreateBr(meetBlock);

    Value *elseV = nullptr;
    // 生成else块
    parentFun->getBasicBlockList().push_back(elseBlock);
    builder.SetInsertPoint(elseBlock);
    if (elseExpr != nullptr) {
        auto elseScopeGuard = theScope.guard();
        elseV = elseExpr->codegen();
        if (elseV == nullptr) return nullptr;
    }

    // 获得最新的插入block
    elseBlock = builder.GetInsertBlock();
    // XXX: 检测最后一条语句是否br，若不是则 br meet
    if (elseBlock->getTerminator() == nullptr) builder.CreateBr(meetBlock);

    // 决定 if 的返回值类型
    Type *meetTy = BasicType::VoidTy;
    Type *thenTy = thenV ? thenV->getType() : nullptr;
    Type *elseTy = elseV ? elseV->getType() : nullptr;
    if (thenTy != nullptr && thenTy == elseTy) { meetTy = thenTy; }

    // 生成meet块
    parentFun->getBasicBlockList().push_back(meetBlock);
    builder.SetInsertPoint(meetBlock);

    // parentFun->print(llvm::errs());

    if (meetTy == BasicType::VoidTy)
        return llvm::UndefValue::get(BasicType::VoidTy);

    // FIXME: 若 then 和 else 返回值不同
    llvm::PHINode *phiNode = builder.CreatePHI(meetTy, 2, "ifTmp");

    // FIXME: 可能返回nullptr
    phiNode->addIncoming(thenV, thenBlock);
    phiNode->addIncoming(elseV, elseBlock);

    return phiNode;
}